/**
 * 最长回文子序列
 *
 * 描述
 * 给定一个字符串，找到其中最长的回文子序列，并返回该序列的长度。
 * 注：回文序列是指这个序列无论从左读还是从右读都是一样的。
 * 本题中子序列字符串任意位置删除k（len(s)>=k>=0）个字符后留下的子串。
 *
 * 数据范围：字符串长度满足1≤n≤1000
 * 进阶：
 * 空间复杂度 O(n^2) ，
 * 时间复杂度 O(n^2)
 *
 *  输入描述：
 * 输入一个字符串
 *
 * 输出描述：
 * 输出最长回文子序列
 */

import java.util.Scanner;

/**
 * 子序列, 看见就想动态规划了
 * dp[i][j] : 表示 i, j 范围内的子序列中最长的回文串长度
 * 这里的状态转移方程有两种情况 :
 *  1. 左右两边相等 : dp[i][j] = dp[i + 1][j - 1] + 2
 *  但是这边也要注意, 也是还有两种情况的, 就是相隔的距离, 不考虑的话可能会导致数组下标越界
 *  2. 左右两边不相等 : dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]), 那就两边找最大的答案了
 * 时间复杂度 : O(n ^ 2)
 * 空间复杂度 : O(n ^ 2)
 */

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        char[] nums = in.next().toCharArray();
        int n = nums.length;
        int[][] dp = new int[n + 1][n + 1];

        // 初始化, 这里要将相等的初始化为 1
        // 因为最终的 dp 数组依赖的是自身左下
        // 角的元素, 所以我们在正式开始状态
        // 转移之前要初始化第一波可能会用到的
        // 位置
        for (int i = 1; i <= n; i++) {
            dp[i][i] = 1;
        }

        for (int j = 1; j <= n; j++) {

            // 这里要注意从后向前面开始遍历, 因为我们的状态
            // 是依赖于左下角的状态的, 所以开始赋值的时候也
            // 需要从下面开始赋值
            for (int i = j - 1; i >= 1 ; i--) {

                // 两边相等
                if (nums[i - 1] == nums[j - 1]) {

                    if (i + 1 < j) {

                        // 两边相差大于 1
                        dp[i][j] = dp[i + 1][j - 1] + 2;
                    } else {

                        // 两边相差 为1
                        dp[i][j] = 2;
                    }
                } else {

                    // 不相等就去两边的最大值
                    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }

        // 返回结果
        System.out.println(dp[1][n]);
    }
}